/*
* Copyright 2015 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.uberfire.backend.server.cdi;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.net.URI;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.Any;
import javax.enterprise.inject.Default;
import javax.enterprise.inject.spi.AfterBeanDiscovery;
import javax.enterprise.inject.spi.AfterDeploymentValidation;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.Extension;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.enterprise.inject.spi.InjectionTarget;
import javax.enterprise.inject.spi.ProcessAnnotatedType;
import javax.enterprise.inject.spi.ProcessBean;
import javax.enterprise.inject.spi.ProcessProducer;
import javax.enterprise.inject.spi.WithAnnotations;
import javax.enterprise.util.AnnotationLiteral;
import javax.inject.Named;
import javax.inject.Singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.uberfire.commons.cluster.ClusterServiceFactory;
import org.uberfire.commons.lifecycle.PriorityDisposableRegistry;
import org.uberfire.commons.services.cdi.Startable;
import org.uberfire.commons.services.cdi.Startup;
import org.uberfire.commons.services.cdi.StartupType;
import org.uberfire.commons.services.cdi.Veto;
import org.uberfire.io.IOService;
import org.uberfire.io.impl.IOServiceNio2WrapperImpl;
import org.uberfire.io.impl.cluster.IOServiceClusterImpl;
import org.uberfire.java.nio.IOException;
import org.uberfire.java.nio.base.FileSystemState;
import org.uberfire.java.nio.file.FileStore;
import org.uberfire.java.nio.file.FileSystem;
import org.uberfire.java.nio.file.FileSystemAlreadyExistsException;
import org.uberfire.java.nio.file.InvalidPathException;
import org.uberfire.java.nio.file.Path;
import org.uberfire.java.nio.file.PathMatcher;
import org.uberfire.java.nio.file.PatternSyntaxException;
import org.uberfire.java.nio.file.WatchService;
import org.uberfire.java.nio.file.attribute.UserPrincipalLookupService;
import org.uberfire.java.nio.file.spi.FileSystemProvider;
public class SystemConfigProducer implements Extension {
private static final Logger logger = LoggerFactory.getLogger(SystemConfigProducer.class);
private static final String CDI_METHOD = "cdi";
private static final String START_METHOD = System.getProperty("org.uberfire.start.method",
"cdi");
private final List<OrderedBean> startupEagerBeans = new LinkedList<OrderedBean>();
private final List<OrderedBean> startupBootstrapBeans = new LinkedList<OrderedBean>();
private final Comparator<OrderedBean> priorityComparator = new Comparator<OrderedBean>() {
@Override
public int compare(final OrderedBean o1,
final OrderedBean o2) {
return o1.priority - o2.priority;
}
};
private boolean systemFSNotExists = true;
private boolean ioStrategyBeanNotFound = true;
public void processSystemFSProducer(@Observes ProcessProducer<?, FileSystem> pp) {
if (pp.getAnnotatedMember().getJavaMember().getName().equals("systemFS")) {
ioStrategyBeanNotFound = false;
}
}
public void processIOServiceProducer(@Observes ProcessProducer<?, IOService> pp) {
if (pp.getAnnotatedMember().getJavaMember().getName().equals("ioStrategy")) {
ioStrategyBeanNotFound = false;
}
}
public <X> void processBean(@Observes final ProcessBean<X> event) {
if (event.getBean().getName() != null && event.getBean().getName().equals("systemFS")) {
systemFSNotExists = false;
} else if (event.getBean().getName() != null && event.getBean().getName().equals("ioStrategy")) {
ioStrategyBeanNotFound = false;
}
if (event.getAnnotated().isAnnotationPresent(Startup.class) && (event.getAnnotated().isAnnotationPresent(ApplicationScoped.class)
|| event.getAnnotated().isAnnotationPresent(Singleton.class))) {
final Startup startupAnnotation = event.getAnnotated().getAnnotation(Startup.class);
final StartupType type = startupAnnotation.value();
final int priority = startupAnnotation.priority();
final Bean<?> bean = event.getBean();
switch (type) {
case EAGER:
startupEagerBeans.add(new OrderedBean(bean,
priority));
break;
case BOOTSTRAP:
startupBootstrapBeans.add(new OrderedBean(bean,
priority));
break;
}
} else if (event.getAnnotated().isAnnotationPresent(Named.class) && (event.getAnnotated().isAnnotationPresent(ApplicationScoped.class)
|| event.getAnnotated().isAnnotationPresent(Singleton.class))) {
final Named namedAnnotation = event.getAnnotated().getAnnotation(Named.class);
if (namedAnnotation.value().endsWith("-startable")) {
final Bean<?> bean = event.getBean();
startupBootstrapBeans.add(new OrderedBean(bean,
10));
}
}
}
public void afterDeploymentValidation(final @Observes AfterDeploymentValidation event,
final BeanManager manager) {
if (CDI_METHOD.equalsIgnoreCase(START_METHOD)) {
//Force execution of Bootstrap bean's @PostConstruct methods first
runPostConstruct(manager,
startupBootstrapBeans);
//Followed by execution of remaining Eager bean's @PostConstruct methods
runPostConstruct(manager,
startupEagerBeans);
}
}
private void runPostConstruct(final BeanManager manager,
final List<OrderedBean> orderedBeans) {
//Sort first, by priority
Collections.sort(orderedBeans,
priorityComparator);
for (OrderedBean ob : orderedBeans) {
// the call to toString() is a cheat to force the bean to be initialized
final Bean<?> bean = ob.bean;
manager.getReference(bean,
bean.getBeanClass(),
manager.createCreationalContext(bean)).toString();
}
}
<T> void processAnnotatedType(@Observes @WithAnnotations(Veto.class) ProcessAnnotatedType<T> pat) {
pat.veto();
}
void afterBeanDiscovery(@Observes final AfterBeanDiscovery abd,
final BeanManager bm) {
if (systemFSNotExists) {
buildSystemFS(abd,
bm);
}
if (ioStrategyBeanNotFound) {
buildIOStrategy(abd,
bm);
}
if (!CDI_METHOD.equalsIgnoreCase(START_METHOD)) {
buildStartableBean(abd,
bm);
}
}
private void buildSystemFS(final AfterBeanDiscovery abd,
final BeanManager bm) {
final InjectionTarget<DummyFileSystem> it = bm.createInjectionTarget(bm.createAnnotatedType(DummyFileSystem.class));
abd.addBean(createFileSystemBean(bm,
it));
}
Bean<FileSystem> createFileSystemBean(final BeanManager bm,
final InjectionTarget<DummyFileSystem> it) {
return new Bean<FileSystem>() {
@Override
public Class<?> getBeanClass() {
return FileSystem.class;
}
@Override
public Set<InjectionPoint> getInjectionPoints() {
return it.getInjectionPoints();
}
@Override
public String getName() {
return "systemFS";
}
@Override
public Set<Annotation> getQualifiers() {
return new HashSet<Annotation>() {{
add(new AnnotationLiteral<Default>() {
});
add(new AnnotationLiteral<Any>() {
});
add(new NamedLiteral("systemFS"));
}};
}
@Override
public Class<? extends Annotation> getScope() {
return ApplicationScoped.class;
}
@Override
public Set<Class<? extends Annotation>> getStereotypes() {
return Collections.emptySet();
}
@Override
public Set<Type> getTypes() {
return new HashSet<Type>() {{
add(FileSystem.class);
add(Object.class);
}};
}
@Override
public boolean isAlternative() {
return false;
}
@Override
public boolean isNullable() {
return false;
}
@Override
public FileSystem create(CreationalContext<FileSystem> ctx) {
final Bean<IOService> bean = (Bean<IOService>) bm.getBeans("configIO").iterator().next();
final CreationalContext<IOService> _ctx = bm.createCreationalContext(bean);
final IOService ioService = (IOService) bm.getReference(bean,
IOService.class,
_ctx);
FileSystem systemFS;
try {
systemFS = ioService.newFileSystem(URI.create("git://system"),
new HashMap<String, Object>() {{
put("init",
Boolean.TRUE);
put("internal",
Boolean.TRUE);
}});
} catch (FileSystemAlreadyExistsException e) {
systemFS = ioService.getFileSystem(URI.create("git://system"));
}
PriorityDisposableRegistry.register("systemFS",
systemFS);
return systemFS;
}
@Override
public void destroy(final FileSystem instance,
final CreationalContext<FileSystem> ctx) {
try {
instance.dispose();
PriorityDisposableRegistry.unregister("systemFS");
} catch (final Exception ex) {
logger.warn(ex.getMessage(),
ex);
}
ctx.release();
}
};
}
private void buildIOStrategy(final AfterBeanDiscovery abd,
final BeanManager bm) {
final InjectionTarget<IOServiceNio2WrapperImpl> it = bm.createInjectionTarget(bm.createAnnotatedType(IOServiceNio2WrapperImpl.class));
abd.addBean(new Bean<IOService>() {
@Override
public Class<?> getBeanClass() {
return IOService.class;
}
@Override
public Set<InjectionPoint> getInjectionPoints() {
return it.getInjectionPoints();
}
@Override
public String getName() {
return "ioStrategy";
}
@Override
public Set<Annotation> getQualifiers() {
return new HashSet<Annotation>() {{
add(new AnnotationLiteral<Default>() {
});
add(new AnnotationLiteral<Any>() {
});
add(new NamedLiteral("ioStrategy"));
}};
}
@Override
public Class<? extends Annotation> getScope() {
return ApplicationScoped.class;
}
@Override
public Set<Class<? extends Annotation>> getStereotypes() {
return Collections.emptySet();
}
@Override
public Set<Type> getTypes() {
return new HashSet<Type>() {{
add(IOService.class);
add(Object.class);
}};
}
@Override
public boolean isAlternative() {
return false;
}
@Override
public boolean isNullable() {
return false;
}
@Override
public IOService create(CreationalContext<IOService> ctx) {
final Bean<ClusterServiceFactory> clusterFactoryBean = (Bean<ClusterServiceFactory>) bm.getBeans("clusterServiceFactory").iterator().next();
final CreationalContext<ClusterServiceFactory> _ctx = bm.createCreationalContext(clusterFactoryBean);
final ClusterServiceFactory clusterServiceFactory = (ClusterServiceFactory) bm.getReference(clusterFactoryBean,
ClusterServiceFactory.class,
_ctx);
final IOService result;
if (clusterServiceFactory == null) {
result = new IOServiceNio2WrapperImpl();
} else {
result = new IOServiceClusterImpl(new IOServiceNio2WrapperImpl(),
clusterServiceFactory);
}
return result;
}
@Override
public void destroy(final IOService instance,
final CreationalContext<IOService> ctx) {
ctx.release();
}
});
}
private void buildStartableBean(final AfterBeanDiscovery abd,
final BeanManager bm) {
abd.addBean(new Bean<Startable>() {
@Override
public Class<?> getBeanClass() {
return Startable.class;
}
@Override
public Set<InjectionPoint> getInjectionPoints() {
return Collections.emptySet();
}
@Override
public String getName() {
return "startablebean";
}
@Override
public Set<Annotation> getQualifiers() {
return new HashSet<Annotation>() {{
add(new AnnotationLiteral<Default>() {
});
add(new AnnotationLiteral<Any>() {
});
}};
}
@Override
public Class<? extends Annotation> getScope() {
return ApplicationScoped.class;
}
@Override
public Set<Class<? extends Annotation>> getStereotypes() {
return Collections.emptySet();
}
@Override
public Set<Type> getTypes() {
return new HashSet<Type>() {{
add(Startable.class);
add(Object.class);
}};
}
@Override
public boolean isAlternative() {
return false;
}
@Override
public boolean isNullable() {
return false;
}
@Override
public Startable create(CreationalContext<Startable> ctx) {
return new Startable() {
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public void start() {
//Force execution of Bootstrap bean's @PostConstruct methods first
runPostConstruct(bm,
startupBootstrapBeans);
//Followed by execution of remaining Eager bean's @PostConstruct methods
runPostConstruct(bm,
startupEagerBeans);
}
};
}
@Override
public void destroy(final Startable instance,
final CreationalContext<Startable> ctx) {
ctx.release();
}
});
}
public static class DummyFileSystem implements FileSystem {
private FileSystemState state = FileSystemState.NORMAL;
@Override
public FileSystemProvider provider() {
return null;
}
@Override
public boolean isOpen() {
return false;
}
@Override
public boolean isReadOnly() {
return false;
}
@Override
public String getSeparator() {
return null;
}
@Override
public Iterable<Path> getRootDirectories() {
return null;
}
@Override
public Iterable<FileStore> getFileStores() {
return null;
}
@Override
public Set<String> supportedFileAttributeViews() {
return null;
}
@Override
public Path getPath(String first,
String... more) throws InvalidPathException {
return null;
}
@Override
public PathMatcher getPathMatcher(String syntaxAndPattern) throws IllegalArgumentException, PatternSyntaxException, UnsupportedOperationException {
return null;
}
@Override
public UserPrincipalLookupService getUserPrincipalLookupService() throws UnsupportedOperationException {
return null;
}
@Override
public WatchService newWatchService() throws UnsupportedOperationException, IOException {
return null;
}
@Override
public void close() throws IOException {
}
@Override
public void dispose() {
}
}
private class OrderedBean {
Bean<?> bean;
int priority;
private OrderedBean(final Bean<?> bean,
final int priority) {
this.bean = bean;
this.priority = priority;
}
}
private class DummyStarable implements Startable {
@Override
public void start() {
}
}
public class NamedLiteral extends AnnotationLiteral<Named> implements Named {
private final String value;
public NamedLiteral(String value) {
this.value = value;
}
@Override
public String value() {
return value;
}
}
}